package com.example.switchyard.docs; import org.switchyard.component.bean.Service; @Service(Example.class) public class ExampleBean implements Example { }
The Bean Component is a pluggable container in SwitchYard which allows Java classes (or beans) to provide and consume services. This means that you can implement a service by simply annotating a Java class. It also means you can consume a service by injecting a reference to that service directly into your Java class. Rather than writing our own POJO container to provide this capability, we have implemented the bean component as a Weld extension. No need to learn a new programming model - bean services are standard CDI beans with a few extra annotations. This also opens up the possibilities of how SwitchYard is used; you can now expose existing CDI-based beans in your application as services to the outside world or consume services within your bean
To create a new Bean service you only need a few pieces of information:
Name : the name of the Java class for your bean service.
Service Name : the name of the service your bean will provide.
Interface : the contract for the service being provided. Java is the only valid interface type for bean services.
Note that starting with the interface value will default the names for Name and Service Name, which can speed things up if you're in a hurry.
After clicking Finish, you will have a new class that looks something like this:
package com.example.switchyard.docs; import org.switchyard.component.bean.Service; @Service(Example.class) public class ExampleBean implements Example { }
The @Service annotation allows the SwitchYard CDI Extension to discover your bean at runtime and register it as a service. The value of the annotation (Example.class in the above example) represents the service contract for the service. Every bean service must have an @Service annotation with a value identifying the service interface for the service.
To complete the definition of your service, add one or more operations to the service interface and provide a corresponding implementation in your bean:
package com.example.switchyard.docs; import org.switchyard.component.bean.Service; @Service(Example.class) public class ExampleBean implements Example { public void greet(Person person) { // implement service logic here for greet operation } }
In addition to providing a service in SwitchYard, beans can also consume other services. These services can be provided in the same application by other implementations, or they could be wired to gateway bindings to invoke services over JMS, SOAP, FTP, etc. The SwitchYard runtime handles the resolution of the service reference to a concrete service, allowing your service logic to remain blissfully ignorant. Invocations made through this reference are routed through the SwitchYard exchange mechanism.
Consuming a SwitchYard service from within a CDI bean is done via @Reference annotations.
@Service(ConsumerService.class) public class ConsumerServiceBean implements ConsumerService { @Inject @Reference private SimpleService service; public void consumeSomeService(String consumerName) { service.sayHello("Hello " + consumerName); } }
By default, SwitchYard expects a service reference to be declared with a name which matches the Java type used for the reference. In the above example, the SimpleService type would require a service reference in your SwitchYard configuration called "SimpleService". In the event where the service reference name is different from the type name of the contract, the @Reference annotation can accept a service name:
@Reference("urn:myservices:purchasing:OrderService") private OrderService orders;
While it's a best practice to write your service logic to the data that's defined in the contract (the input and output message types), there can be situations where you need to access contextual information like message headers (e.g. received file name) in your implementation. To facilitate this, the Bean component allows you to access the SwitchYard Exchange Context instance associated with a given Bean Service Operation invocation. To get a reference to the Context, simply add a Context property to your bean and annotate it with the CDI @Inject annotation.
@Service(SimpleService.class) public class SimpleServiceBean implements SimpleService { @Inject private Context context; public String sayHello(String message) { System.out.println("*** Funky Context Property Value: " + context.getPropertyValue("funkyContextProperty")); return "Hi there!!"; } }
The Context interface allows your bean logic to get and set properties in the context. Note that you can only make calls on the Context instance within the scope of one of the Service Operation methods. Invoking it outside this scope will result in an UnsupportedOperationException being thrown.
In contrast to invocation properties, implementation properties represent environmental properties that you have defined in the SwitchYard application descriptor (switchyard.xml) for your bean implementation. To access these properties, simply add an @Property annotation to your bean class identifying the property you want to inject. The following example demonstrates injection of a "user.name" property:
@Service(SimpleService.class) public class SimpleServiceBean implements SimpleService { @Property(name="user.name") private String name; public String sayHello(String message) { return "Hello " + name + ", I got a message: " + message; } }
See the Properties section of the User Guide for more information about SwitchYard property support.
Camel services allow you to leverage the core routing engine inside of Apache Camel to route between services in SwitchYard. All of the EIP and core routing support in Camel is available to your service implementation. Each Camel route is exposed as a service within SwitchYard, which means it has a well-defined contract and can be injected into any other service in the runtime.
The first thing you need to decide when you create a Camel routing service is whether you want to use the Java DSL or XML dialect for the route. Functionally, they are more or less equivalent, so it's more a choice of how you want to express your routing logic. If you want create a Java DSL route, select the "Camel (Java)" implementation type. For XML, use the "Camel (XML)" type. Regardless of which language you choose, the following information is required:
Name : the name of the Java class or XML file for your bean service.
Service Name : the name of the service your bean will provide.
Interface : the contract for the service being provided. Camel supports Java, WSDL, and ESB contract types.
For details on each type of Camel route, see the Java DSL Routes and XML Routes sections below. You can have multiple routing services per application, each with it's own routing language (e.g. an application can have two Java DSL routes and one XML route). There are some general guidelines to keep in mind with both types of routes:
There is only one route per service.
The consumer or "from" endpoint in a route is always a "switchyard" endpoint and the endpoint name must equal the service name. This is default behavior in the tooling.
To consume other services from within your route, only use "switchyard" consumer (i.e. "to") endpoints. This keeps your routing logic independent of the binding details for consumed services.
A newly created Java DSL route looks like this:
package com.example.switchyard.docs; import org.apache.camel.builder.RouteBuilder; public class CamelServiceRoute extends RouteBuilder { /** * The Camel route is configured via this method. The from: * endpoint is required to be a SwitchYard service. */ public void configure() { // TODO Auto-generated method stub from("switchyard://Example").log( "Received message for 'Example' : ${body}"); } }
There are no SwitchYard-specific classes or APIs used for Java DSL routes; the route class is identical in signature to what you would use with Apache Camel directly. Since there is one route per service, you will have one RouteBuilder class for each Camel routing service in your application. To add logic to your routing service, simply add additional logic to the configure() method. For background and details on what you can do with the Java DSL, please consult the Apache Camel documentation.
A newly created XML route looks like this:
<?xml version="1.0" encoding="ASCII"?> <route xmlns="http://camel.apache.org/schema/spring"> <from uri="switchyard://Example"/> <log message="Example - message received: ${body}"/> </route>
Like Java DSL routes, the XML routing syntax is identical to what you would use with Apache Camel directly and conforms to the Camel schema for <route> definitions. There will be one file containing a route definition for each XML routing service in your application.
Invoking another service from within your Camel route can be done by using the SwitchYard producer endpoint (switchyard://) within your route. Endpoint configuration is very straightforward:
switchyard://[service-name]?operationName=[operation-name]
service-name : name of the SwitchYard service. This value needs to match the name of a service reference defined on the service component for the route.
operation-name : name of the service operation to be invoked. This is only used on references and is optional if the target service only has a single operation.
A modified version of the default XML route which invokes a SwitchYard service can be found below:
<?xml version="1.0" encoding="ASCII"?> <route xmlns="http://camel.apache.org/schema/spring"> <from uri="switchyard://Example"/> <log message="Example - message received: ${body}"/> <!-- Invoke hasItem operation on WarehouseService --> <to uri="switchyard://WarehouseService?operationName=hasItem"/> </route>
Camel supports dynamic languages inside route logic in both XML and Java form. As the support for dynamic languages is built in JDK any language which provides javax.script.ScriptEngineManager may be used. However, because of 3rd party dependencies by default SwitchYard supports only following languages:
BeanShell
JavaScript
Groovy
Ruby
Python
To use these script languages you have couple of options. They are available in expression-aware places like filter:
public class ScriptingBuilder extends RouteBuilder { public void configure() { from("switchyard://Inbound") .filter().javaScript("request.getHeader('myHeader') != null") .to("switchyard://Outbound"); } }
If you would like use dynamic language to implement your service you may use transform element:
public class ScriptingImplementationBuilder extends RouteBuilder { public void configure() { from("switchyard://Inbound") .transform().groovy("classpath:script.groovy"); // classpath resource from("switchyard://InboundBsh") .transform().language("beanshell", "file:script.bsh"); // file system resource } }
Inside your script you will have access to predefined variables like request, response or exchange which will let you generate response.
SwitchYard integrates the CDI Bean Manager with the Camel Bean Registry to allow you to reference CDI Beans in your Camel routes. Any Java class annotated with @Named in your application will be available through Camel's Bean registry.
Consider an example of where you have the following CDI bean:
@Named("StringSupport") @ApplicationScoped public class StringUtil { public String trim(String string) { return string.trim(); } }
This bean can be used inside your SwitchYard Camel Routes as follows:
public class ExampleBuilder extends RouteBuilder { public void configure() { from("switchyard://ExampleBuilder") .split(body(String.class).tokenize("\n")) .filter(body(String.class).startsWith("sally:")) .to("bean:StringSupport"); } }
See Camel's Bean Binding documentation for more details.
SwitchYard integrates with the Properties Component in Camel to make system and application properties available inside your route definitions. You can inject properties into your camel route using "{{propertyName}}" expression where "propertyName" is the name of the property. The following camel route expects the "user.name" property to be injected in the last <log> statement:
<route xmlns="http://camel.apache.org/schema/spring" id="CamelTestRoute"> <log message="ItemId [${body}]"/> <to uri="switchyard://WarehouseService?operationName=hasItem"/> <log message="Title Name [${body}]"/> <log message="Properties [{{user.name}}]"/> </route>
See the Properties section of the User Guide for more details of SwitchYard property support.
The BPM Component is a pluggable container in SwitchYard which allows a business process to be exposed as a service. One fronts their business process with a custom interface and, if desired, can easily annotate it's methods to define which should start a process, signal a process event, or abort a process.
A BPM Service is a type of Knowledge Service (the other type being a Rules Service). Thus, it is strongly suggested you familiarize yourself with the shared configuration capabilities of Knowledge Services.
To create a new BPM service in SwitchYard, you'll need the following information:
File Name : the file name that will be used to create a new, empty BPMN 2 Process definition.
Interface Type : the contract for the service being provided. BPM supports Java, WSDL, and ESB contract types.
Service Name : the name of the service your process will provide.
After clicking Finish, you will have a new service component definition for your process service and an empty BPMN process definition. The next phase in developing your BPM service is to author the BPMN 2 process definition (details outside the scope of this documentation) and then configure the BPM service component to interact with that process definition.
Interaction with a process is defined via actions on a BPM service component. Take the following service contract as an example:
package org.switchyard.userguide; public interface MyService { public void start(String data); public void signal(String data); public void stop(String data); }
Actions allow you to map an operation in the service contract to one of the following interactions with a business process:
START_PROCESS
SIGNAL_EVENT
ABORT_PROCESS_INSTANCE
Operations configured with the START_PROCESS action type will start new process instances.
When you start your process (actually, any interaction with a service whose implementation is bpm), the processInstanceId will be put into the Switchyard Context at Scope.EXCHANGE, and will be fed back to your client in a binding-specific way. For soap, it will be in the form of a soap header in the soap response envelope:
<soap:Header> <bpm:processInstanceId xmlns:bpm="urn:switchyard-component-bpm:bpm:1.0">1</bpm:processInstanceId> </soap:Header>
In future process interactions, you will need to send back that same processInstanceId, so that the correlation is done properly. For soap, that means including the same soap header that was returned in the response to be sent back with subsequent requests.
If you are using persistence, the sessionId will also be available in the Context, and will need to be fed back as well. It would look the same way in the soap header as the processInstanceId does.
Operations configured with the SIGNAL_EVENT action type will have the associated process instance signaled. As above, the processInstanceId will need to have been available in the Context so the correct process instance is correlated.
There are two other pieces of information when signaling an event:
The "id". In BPMN2 lexicon, this is known as the "signal id", but in jBPM can also be known as the "event type". This is set as the id of the annotation.
Note: In BPMN2, a signal looks like this: <signal id="foo" value="bar"/> In jBPM, it is the signal id that is respected, not the name. This might require you to tweak a tooling-generated id to whatever you want it called.
The "event object". This is the data representing the event itself. There are two ways to pass in the event object:
Via a Context object, passed similar to the processInstanceId, known as "signalEvent". If this is the case, you are limited to a String type.
Via the Message content object itself (your payload). If the signalEvent Context property is absent, the content of the Message is used as the event object.
Operations configured with the ABORT_PROCESS_INSTANCE action type will cause associated process instances to be aborted. As above, the processInstanceId will need to have been available in the Context so the correct process instance is correlated.
For you to be able to use variables inside your process, you have to declare your variable names at the process level, as well as in the Parameter Mapping (and possibly Result Mapping) of any process node that wants access to those variables. This can be done easily in the jBPM Eclipse tooling by using the Properties view when clicking on either the whitespace around the process, or on any of your process nodes. For more information, please refer to the jBPM documentation.
Mapping variables from your SwitchYard Exchange, Context or Message into jBPM process variables can be done via expressions, the default (and only currently supported) type is MVEL. Each action for a process service has a distinct set of variable mappings.
For more information on mapping variables, please see the Mappings sections of the Knowledge Services documentation.
There are two ways of consuming Services with the SwitchYard BPM component:
Invoking the BPM implementation through a gateway binding. Since the BPM component exposes a java interface fronting the business process, you can use any of the bindings provided by SwitchYard. This could be a SOAP Binding or a Camel Binding, for example. (Please refer to those sections of this guide for more information.)
Invoking other SwitchYard Services from inside a BPM process itself. To do this, you can use the SwitchYardServiceWorkItemHandler, which is provided out-of-the-box. To make authoring BPMN2 processes easier, SwitchYard provides a new widget for the Eclipse BPMN2 Modeler visual editor palette. Here is a screenshot from the "Help Desk" demo quickstart:
On the bottom right hand side under "Custom Task", you can see the SwitchYard Service widget. On the left hand side, you can see the various points of the business process where SwitchYard Services are being invoked. Once you have dropped a SwitchYard Service task in the main window, you can customize it via the Eclipse Properties Editor.
The following are properties you can use to configure the SwitchYard Service task (a.k.a. the "Dynamic" SwitchYard Service task):
Service Naming Properties
ServiceName: (required) The name of the SwitchYard service to invoke.
ServiceOperationName: (optional; default=use the single method name in the service interface, if there is just one) The name of the operation within the SwitchYard service to invoke.
Content I/O Properties
ContentInputName: (optional; default=contentInput) The process variable which the message content will be placed in.
ContentOutputName: (optional; default=contentOutput) The process variable which the message content will be gotten from.
Fault-Handling Properties (see SwitchYard Service Fault Handling below)
FaultResultName: (optional) The name of the output parameter (result variable) the fault (Exception) will be stored under.
FaultSignalId: (optional) The bpmn signal id ("event type" in jbpm lingo) that will be used to signal an event in the same process instance. The event object will be the fault (Exception). Please see Signaling a Process above.
FaultWorkItemAction: (optional; default=null) After a fault occurs, what should be done? If null, nothing is done. If complete, the current work item (SwitchYard Service task) is completed. If abort, the current work item is aborted.
You can read a more detailed explanation of the Help Desk quickstart demo, as well as how to set up Eclipse to make use of new widget, on this wiki page.
During your process execution, the SwitchYardServiceWorkItemHandler class is used to handle Switchyard Service tasks. It executes service references, per the properties specified in the panel above. Pay particular attention to the list of Fault-Handling Properties. These properties define the behavior of the SwitchYardServiceWorkItemHandler in the case were a fault is encountered when executing the service reference. There are 2 scenarios that the fault-handling covers:
"In my process flow, after the SwitchYard Service task, I would like a split gateway where I can inspect a process variable (a "flag") that can tell me that a fault occurred, so that I can diverge one way or another."
To do this, specify the FaultResultName property. The SwitchYardServiceWorkItemHandler will make the fault itself available as an output parameter of the task, so that it can be associated with a process variable, and inspected for existence in your split gateway. You must also set the FaultWorkItemAction property to complete, so that the process will continue on to your split gateway!
An example bpmn2 process can be found in our JUnit test suite here: BPMWorkTests-FaultResultProcess.bpmn
"In my process flow, I have multiple places where faults could occur, and I want to have one shared path of fault-handling."
To do this, specify the FaultSignalId property. This must match a signal id you specify in your bpmn2 process definition. You can then add an event node in your process that is triggered with this signal id; the flow from that event node on is your fault-handling path. The SwitchYardServiceWorkItemHandler will then signal the proper event with the configured id.
An example bpmn2 process can be found in our JUnit test suite here: BPMWorkTests-FaultEventrocess.bpmn
Whether you choose scenario 1 or 2 above, the question remains "What next?" If you don't specify a FaultWorkItemAction property, nothing is done. The work item is not completed, nor is it aborted. You can set the property to complete to complete the work item after a fault, or you can set the property to abort to abort the work item after a fault.
The FaultWorkItemAction property was added in SwitchYard 0.8, replacing the CompleteAfterFault property of 0.7 and earlier, at which point the default was to complete the work item.
It is possible to invoke SwitchYard Services using the standard BPMN2 Service Task <serviceTask>. It is conceptually similar, however you will use the Service Task icon from the BPMN2 Editor palette, and configure it slightly differently:
The <serviceTask> knows to invoke SwitchYard when it has an implementation="##SwitchYard" attribute.
The ServiceName is derived from the BPMN2 interfaceImplementationRef.
The ServiceOperationName is derived from the BPMN2 operationImplementationRef.
The ContentInputName is always called Parameter.
The ContentOutputName is always called Result.
A Resource represents an artifact that is required by your BPM process. It could be anything you could think of, including a properties file, a Drools Rule Language file, or whatever. But it needs to be available to the BPM component's runtime for that process. The list of resources available to your process is a configurable aspect of the BPM service component.
A WorkItemHandler is a way for you to add your own code into the business process. Simply implement the org.kie.runtime.process.WorkItemHandler interface and add a handler definition to your BPM service component.
By default, the SwitchYardServiceWorkItemHandler is always added for you by the SwitchYard Maven plugin. That handler allows you to easily call out to other SwitchYard services by name within your business process. Please see the section "Consuming a BPM Service" below for more information.
Please see the Listeners and Loggers sections found in the Knowledge Services documentation.
The Rules Component is a pluggable container in SwitchYard which allows business rules to be exposed as a service. One fronts their rules with a custom interface and, if desired, can easily annotate it's methods to define which should execute the rules. The Rules Component currently supports Drools as the rule engine. Even though it is quite simple to write rules in Drools, that project's developer tooling and business analyst tooling are very mature.
A Rules Service is a type of Knowledge Service (the other type being a BPM Service). Thus, it is strongly suggested you familiarize yourself with the shared configuration capabilities of Knowledge Services.
To create a new Rules service in SwitchYard, you'll need the following information
File Name : the file name that will be used to create a new template rules definition.
Interface Type : the contract for the service being provided. Rules services support Java, WSDL, and ESB contract types.
Service Name : the name of the service your process will provide.
Package Name : package name used for the new Rules file.
The MyService interface can be as simple as this, with no SwitchYard-specific imports:
package com.example.switchyard.docs; public interface Example { public void process(MyData data); }
The generated rule template will look like this:
package com.example.switchyard.docs import org.switchyard.Message global Message message rule "RulesExample" when // insert conditional here then // insert consequence here System.out.println("service: ExampleService, payload: " + message.getContent()); end
By default, service method invocation will create a new Drools knowledge session, execute it given the passed-in domain data, and then be cleanly disposed.
However, it is possible to configure SwitchYard so that a stateful knowledge session will be used. Specifically, it will "stick around" spanning multiple invocations. (Please visit the Drools documentation to learn more about stateless vs. stateful knowledge sessions.) To do this, you use the FIRE_ALL_RULES action type instead of EXECUTE.
There is also a new capability which allows you to insert facts into a stateful knowledge session without firing the rules (you might want to do that later). In this case, use the INSERT action type.
Mapping variables from your SwitchYard Exchange, Context or Message into Drools Globals can be done via expressions, the default (and only currently supported) type is MVEL. This can be done by configuring the rules implementation:
Your expression can use the variables exchange (org.switchyard.Exchange), context (org.switchyard.Context) or message (org.switchyard.Message). You can see in the example above is that context can be accessed in the expression as a java.util.Map. When accessing it as such, the default properties Scope is IN. This can be overridden using the contextScope attribute by changing it to OUT or EXCHANGE.
Now you can use these global variables in your Drools DRL:
package example global java.lang.String service global java.lang.String messageId global com.example.Payload payload rule "Example" when ... then ... System.out.println("service: " + service + ", messageId: " + messageId + ", payload: " + payload); end
Of course, in a more realistic scenario, the payload would be accessed in rule firing because it was inserted into the session directly by the RulesExchangeHandler, and thus evaluated (rather than being accessed as a global). See Mapping Facts below.
The default Object which is inserted into the rules engine as a fact is the SwitchYard Message's content. However, this can be overridden by specifying your own fact mappings.
IMPORTANT: If you specify your own fact mappings, the SwitchYard Message content will not be inserted as a fact. You will have to add a fact mapping with an expression of "message.content" to have it still included.
There is no point in specifying the "variable" attribute of the mappings (as done for global mappings), as the result of each expression is inserted as a nameless fact. For stateless execution, the full list of facts is passed to the StatelessKnowledgeSessions' execute(Iterable) method. For stateful execution, each fact is individually inserted into the StatefulKnowledgeSession. And for Complex Event Processing, each fact is individually inserted into the WorkingMemoryEntryPoint.
If the result of the mapping expression implements Iterable (for example, a Collection), then the result is iterated over, and each iteration is inserted as a separate fact, rather than the parent Iterable itself being inserted. This is not recursive behavior (i.e: it is only done once).
Complex Event Processing, or "CEP", is an advanced topic, and can best be explained in the Drools Fusion documentation. What will be shown here in the SwitchYard documentation is how it can be configured via XML:
<implementation.rules ...> <actions> <action id="FooStream" operation="processFooMessage" type="FIRE_UNTIL_HALT"/> <action id="BarStream" operation="processBarMessage" type="FIRE_UNTIL_HALT"/> </actions> ... <properties> <property name="drools.clockType" value="realtime"/> <property name="drools.eventProcessingMode" value="stream"/> <property name="drools.maxThreads" value="1"/> <property name="drools.multithreadEvaluation" value="false"/> </properties> </implementation.rules>
Please see the Listeners and Loggers sections found in the Knowledge Services documentation.
Please see the Channels section found in the Knowledge Services documentation.
The BPEL Component is a pluggable container in SwitchYard which allows a WS-BPEL business process to be exposed as a service through an interface defined using WSDL.
To provide a service with the BPEL component, you will have to:
Define your process using WS-BPEL (e.g. using the Eclipse BPEL editor bundled with JBossTools).
Define a WSDL interface for the BPEL service.
Define a Deployment Descriptor (e.g. using the ODE Deployment Descriptor editor bundled with JBossTools).
Add the component containing the implementation and service interface to the SwitchYard configuration.
Here is an example of the component section of the SwitchYard configuration:
<sca:component name="SayHelloService"> <bpel:implementation.bpel process="sh:SayHello" /> <sca:service name="SayHelloService"> <sca:interface.wsdl interface="SayHelloArtifacts.wsdl#wsdl.porttype(SayHello)"/> </sca:service> </sca:component>
The BPEL component contains a single 'implementation.bpel' element that identifies the fully qualified name of the BPEL process.
The component may also contain one or more service elements defining the WSDL port types through which the BPEL process can be accessed.
In the packaged Switchyard application, the BPEL process associated with this fully qualified name, must be present within the root folder of the distribution, along with the deployment descriptor (deploy.xml). An example of the deployment descriptor for the BPEL process referenced above is:
<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03" xmlns:examples="http://www.jboss.org/bpel/examples" > <process name="examples:SayHello"> <active>true</active> <retired>false</retired> <process-events generate="all"/> <provide partnerLink="client"> <service name="examples:SayHelloService" port="SayHelloPort"/> </provide> </process> </deploy>
This section describes how a BPEL process can invoke other services.
The first step is to define the WSDL interface(s), representing the service(s) to be consumed, using an invoke element within the deployment descriptor, e.g.
<process name="ls:loanApprovalProcess"> <active>true</active> <process-events generate="all"/> <provide partnerLink="customer"> <service name="ls:loanService" port="loanService_Port"/> </provide> <invoke partnerLink="assessor" usePeer2Peer="false"> <service name="ra:riskAssessor" port="riskAssessor_Port"/> </invoke> </process>
The 'usePeer2Peer' property informs the BPEL engine not to use internal communications for sending messages between BPEL processes that may be executing within the same engine, and instead pass messages through the SwitchYard infrastructure.
For each consumed service, we then need to create a reference element within the SwitchYard configuration, to locate the WSDL file and identify the port type associated with the required WSDL service/port.
<sca:component name="loanService"> <bpel:implementation.bpel process="ls:loanApprovalProcess" /> <sca:service name="loanService"> <sca:interface.wsdl interface="loanServicePT.wsdl#wsdl.porttype(loanServicePT)"/> </sca:service> <sca:reference name="riskAssessor" > <sca:interface.wsdl interface="riskAssessmentPT.wsdl#wsdl.porttype(riskAssessmentPT)"/> </sca:reference> </sca:component>
You can inject properties into your BPEL process definition with using SwitchYardPropertyFunction.resolveProperty() XPath custom function. This bpel:copy section copies "Greeting" property value into the ReplySayHelloVar variable:
..... <bpel:copy> <bpel:from xmlns:property="java:org.switchyard.component.bpel.riftsaw.SwitchYardPropertyFunction" expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"> <![CDATA[concat(property:resolveProperty('Greeting'), $ReceiveSayHelloVar.parameters/tns:input)]]> </bpel:from> <bpel:to part="parameters" variable="ReplySayHelloVar"> <bpel:query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"><![CDATA[tns:result]]></bpel:query> </bpel:to> </bpel:copy>
See Properties section of the User Guide for more details of SwitchYard property support.
BPEL processes can be used to implement long lived stateful business processes. However the BPEL process may need to change, over the course of its lifetime, to accomodate new requirements.
This introduces the problem of how to deal with existing active instances of the BPEL process that may not complete for weeks, months or even years. In these situations we need to have a strategy for dealing with multiple version of a BPEL process, to enable new requirements to be introduced, while still preserving the original process definitions associated with existing active process instances.
This is achieved by simply associating a version number with the BPEL process by adding it as a suffix to the BPEL file name. For example, if our BPEL process would normally be located in the file 'HelloWorld.bpel', then we simply add a hyphen followed by the version number, e.g. 'HelloWorld-32.bpel' would indicate that this is the 32nd version of this BPEL process.
When a new version of the BPEL process has been defined, it is packaged in the SwitchYard application, along side the previous versions of the BPEL process. It is important that the older version of the BPEL process remain in the SwitchYard application until there are no longer any active process instances associated with that version.
It is then important that the SwitchYard application is re-deployed, without undeploying the previous version. If the previous version of the SwitchYard application is undeployed, it will cause the BPEL engine to delete all outstanding active instances associated with the process definitions that have bene removed.
Although the BPEL process is versioned, the WSDL interfaces are not. It is important to ensure that any changes made to the WSDL interfaces are backward compatible, so that both the new and older versions of the BPEL (that still have active process instances) are not affected by the changes.
The following image shows the structure of the say_hello SwitchYard BPEL quickstart:
The important part is how the artifacts are structured within the src/main/resources folder.
The switchyard.xml configuration file is located in the META-INF folder as usual. However the BPEL deployment descriptor (deploy.xml), and the BPEL process definition are located in the root folder.
The WSDL interface definitions, and any accompanying XSD schemas, can be located in sub-folders. If they are, then the BPEL process and SwitchYard BPEL component configuration must define the correct relative path.
Knowledge Services are SwitchYard Services that leverage KIE, and thus, Drools and/or jBPM:
Given that Drools and jBPM are very tightly integrated under KIE, much of their runtime configuration can be shared by both SwitchYard's BPM component and its Rules component. Therefore, documentation on this page refers to elements that are either exactly, or structurally, identical to both the BPM and Rules components. That is to say, any configuration element you see here can be used in the same way for both BPM and Rules. Sometimes, however, the context of the element is significant, so cases like that will be identified where applicable.
For some of the configuration elements below, you might wonder why it is a shared element between both the BPM and Rules components. For example, Channels are something only applicable to Rules, right? Yes, however what if your BPM process has a node which executes business rules, and those rules reference channels? In that case, you need a way to configure channels for the rules within your process. Thus, it is documented here.
Actions are how Knowledge Services know how to map service operation invocations to their appropriate runtime counterparts. For example, when method "myOperation" is called, what should happen? Execute some business rules? Start a business process?
Using the XML below as reference, when the SwitchyYard service’s "myOperation" operation is invoked, an action of type "ACTION_TYPE" will be taken. Note that "ACTION_TYPE" is just a placeholder here. Actual ActionTypes are specific to the BPM and Rules components. Please refer to the specific documentation on those pages.
At this time, the id attribute is only applicable to the Rules component.
Please see the Mapping section below for an explanation of the globals, inputs, and outputs sections.
XML
<actions> <action id="myId" operation="myOperation" type="ACTION_TYPE"> <globals> <mapping/> </globals> <inputs> <mapping/> </inputs> <outputs> <mapping/> </outputs> </action> </actions>
Mappings are the way to move data in or out of the action for that operation. You can specify as many mappings as you like for an action, and they get grouped as globals, inputs or outputs:
Global mappings are used to provide data that is applicable to the entire action, and is often used in classic in/out param (or data-holder/provider) fashion. An example of a global mapping is a global variable specified within a Drools Rule Language (DRL) file.
Input mappings are used to provide data that represents parameters being fed into an action. An example of an input mapping for BPM could be a process variable used while starting a business process. For Rules, it could be a fact to insert into a rules engine session.
Output mappings are used to return data out of an action. An example of an output mapping would be a BPM process variable that you want to set as the outgoing (response) message’s content.
Currently, the only supported expressionType is MVEL, which is the default, so you don’t have to specify it. The expression itself can be any MVEL expression, and variables that are available to you by default are:
exchange - The current org.switchyard.Exchange.
context - The current org.switchyard.Context.
message - The current org.switchyard.Message.
Whatever the resultant value of the expression is constitutes the data that is made available to the action. Some examples:
expression="message.content" - This is the same as message.getContent().
expression="context[‘foo’]" scope="IN" - This is the same as context.getProperty("foo", Scope.IN).getValue(), in a null-safe manner.
Specifying the scope attribute only matters if you use the context variable inside your expression. If you don’t specify a scope, the default Context access (which is done like a Map, if you picked up on that), is done with Scope.EXCHANGE for global mappings, Scope.IN for input mappings, and Scope.OUT for output mappings.
Specifying the variable attribute is often optional, but this depends on the usage. For example, if you are specifying a global variable for a rule, or a process variable to put into (or get out of) a BPM process, then it is required. However, if the result of the expression is to be used as facts for rule session insertion, than specifying a variable name isn’t applicable.
XML
<mapping expression="theExpression" expressionType="MVEL" scope="IN" variable="theVariable"/>
Channels
Drools supports the notion of "Channels", which are basically "exit points" in your DRL. Here is an example:
package com.example rule "example rule" when $f : Foo ( bar > 10 ) then channels["Bar"].send( $f.getBar() ); end
XML
<channels> <channel class="com.example.BarChannel" name="Bar"/> </channels>
Channels must implement org.kie.runtime.Channel.
SwitchYard provides an out-of-the-box Channel which allows you to invoke (one-way) other SwitchYard services directly and easily from your DRL. Here is an example:
XML
<channel name="HelloWorld" reference="HelloWorld" operation="greet"/>
Attribute Reference:
class = The channel implementation class. (Default is SwitchYardServiceChannel.)
name = The channel name. (default = simple name of the implementation class)
reference = The service reference qualified name.
operation = The service reference operation name.
input = The service reference operation input name.
Listeners are used to monitor specific types of events that occur during Knowledge execution. An example of this would be to audit a BPM process, and save the audit details into a database while the process progresses. Then at a later time, these details can be reported against. There are many out-of-the-box Listeners that Drools and jBPM provide, and you can write your own. The only restriction in writing your own Listener is that it must, at the minimum, implement java.util.EventListener. However, your Listener won’t actually be registered for anything unless it also implements one of the respected KIE/Drools/jBPM Listener interfaces. For example, org.drools.event.WorkingMemoryEventListener, org.drools.event.AgendaEventListener, org.kie.event.process.ProcessEventListener, or similar.
If the Listeners provide a Constructor taking a single KieRuntimeEventManager (or KnowledgeRuntimeEventManager) as a parameter, that is used and it is assumed it will do the work of registering itself with the passed-in event manager (OOTB {{WorkingMemoryLogger}}s do this). Otherwise, a no-arg constructor is assumed, and SwitchYard will do the work of registering the Listeners for each of the respected interfaces it implements.
XML
<listeners> <listener class="org.drools.event.DebugProcessEventListener"/> <listener class="org.kie.event.rule.DebugWorkingMemoryEventListener"/> <listener class="com.example.MyListener"/> </listeners>
Loggers
Loggers are special types of Listeners, and are used to output the events that occur during Knowledge execution. Support for Loggers is done using a dedicated configuration element. Events can be logged to the CONSOLE or to a FILE (or THREADED_FILE). If they are directed to a file, that log can later be opened via the Drools Eclipse tooling.
XML
<loggers> <logger interval="2000" log="myLog" type="THREADED_FILE"/> <logger type="CONSOLE/> </loggers>
Manifest
The only configuration element more important than Actions is the Manifest, which is where you specify where the "intelligence" of the component comes from. For the BPM component, this will be, at the minimum, the location of the BPMN 2 process definition file. For the Rules component, this will most likely be the location of DRL, DSL, DSLR or XLS files. There are two ways to to configure the Manifest:
With a KIE Container. This relies upon the existence of a META-INF/kmodule.xml configuration file.
With a manually defined list of Resources.
These two options are mutually exclusive: You have to choose one or the other!
The following examples assume there is a DRL file located at classpath: com/example/MyRules.drl
META-INF/kmodule.xml
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"> <kbase name="com.example"> <ksession name="my-session"/> </kbase> </kmodule>
XML
<manifest> <container sessionName="my-session"/> </manifest>
In addition to the sessionName attribute, you can also specify baseName and releaseId, if desired.
Also, scanning for updates is supported only with the container option (not the resources option). To enable this, simply set scan="true" and, optionally, scanInterval=<# of milliseconds>.
XML
<manifest> <resources> <resource location="com/example/MyProcess.bpmn" type="BPMN2"/> <resource location="com/example/MyRules.drl" type="DRL"/> </resources> </manifest>
If your resources are stored in a repository like Guvnor, they can be loaded remotely. To do so, configure a ChangeSet.xml file as a resource:
<resource location="ChangeSet.xml" type="CHANGE_SET"/>
Then, inside the ChangeSet.xml file, specify the the details of accessing the remote package:
<change-set xmlns='http://drools.org/drools-5.0/change-set' xmlns:xs='http://www.w3.org/2001/XMLSchema-instance' xs:schemaLocation='http://drools.org/drools-5.0/change-set http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd'> <add> <resource source='http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/package/sandbox.michele/LATEST' type='PKG' basicAuthentication="enabled" username="admin" password="admin"/> </add> </change-set>
Properties are the way to provide "hints" to the underlying KIE/Drools/jBPM runtime on how certain options are configured. Rather than having to expose every single KIE/Drools/jBPM option as a configurable element or attribute within SwitchYard, they can be set as open-ended properties.
Properties are an advanced topic, so setting them should be done with care. All possible property names and values will not be listed here, but as a starting point, in your IDE open up a Type Heirarchy with a root of org.kie.conf.Option. That is your full list. Here are just a couple examples:
XML
<properties> <property name="drools.clockType" value="pseudo"/> <property name="drools.eventProcessingMode" value="stream"/> </properties>